package online.hashmall.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import online.hashmall.except.SysException;
import org.springframework.core.MethodParameter;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.HashSet;
import java.util.Set;

/**
 * api异常处理器
 *
 * @author jpq
 */
@Slf4j
@RestControllerAdvice
public class ApiExceptionHandler {

    @ExceptionHandler(SysException.class)
    public R<ApiErrorCode> handleRRException(SysException e) {
        log.error("全局异常信息 ex={}", e.getMessage(), e);
        return R.failed(e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public R<ApiErrorCode> handleNoFoundException(Exception e) {
        log.error("NoHandlerFoundException ex={}", e.getMessage(), e);
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.NO_HANDLER_FOUND.getCode()));
    }

    @ExceptionHandler(DuplicateKeyException.class)
    public R<ApiErrorCode> handleDuplicateKeyException(DuplicateKeyException e) {
        log.error("DuplicateKeyException ex={}", e.getMessage(), e);
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.DUPLICATE_KEY_EXIST.getCode()));
    }

    @ExceptionHandler(IllegalArgumentException.class)
    public R<ApiErrorCode> handleIllegalArgumentException(IllegalArgumentException e) {
        log.error("IllegalArgumentException ex={}", e.getMessage(), e);
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.ILLEGAL_ARGUMENT.getCode()));

    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public R<ApiErrorCode> handleMissingServletRequestParameterException(
            MissingServletRequestParameterException e, ServletRequest request, ServletResponse response) {
        String url = null;
        if (request instanceof HttpServletRequest) {
            HttpServletRequest _request = (HttpServletRequest) request;
            url = _request.getRequestURI();
        }
        log.error("Servlet request parameter invalid ex={}, url={}", e.getMessage(), url, e);
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.MISSING_SERVLET_REQUEST_PARAMETER.getCode()));
    }

    /**
     * 接口参数校验异常处理
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<ApiErrorCode> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        Set<String> errors = new HashSet<>();
        for (ObjectError objectError : bindingResult.getAllErrors()) {
            errors.add(objectError.getDefaultMessage());
        }
        String join = CollUtil.join(errors, StrUtil.COMMA);
        String msg = "数据校验失败:" + join + StrUtil.DOT;
        return R.failed(msg);
    }

    /**
     * 接口参数校验异常处理
     */
    @ExceptionHandler(BindException.class)
    public R<ApiErrorCode> handleMethodArgumentNotValidException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        Set<String> errors = new HashSet<>();
        for (ObjectError objectError : bindingResult.getAllErrors()) {
            errors.add(objectError.getDefaultMessage());
        }
        String join = CollUtil.join(errors, StrUtil.COMMA);
        String msg = "数据校验失败:" + join + StrUtil.DOT;
        return R.failed(msg);
    }

    @ExceptionHandler(Exception.class)
    public R<ApiErrorCode> handleException(Exception e) {
        log.error("全局异常信息 ex={}", e.getMessage(), e);
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.UNKNOWN_ERROR.getCode()));
    }


    /**
     * 方法调用时参数类型不匹配
     *
     * @param e MethodArgumentTypeMismatchException
     */
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public R<ApiErrorCode> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        MethodParameter p = e.getParameter();
        //获知方法，定位问题
        log.error("methodArgumentTypeMismatchException,Method:{},value:{}", p.getMethod(), e.getValue());
        return R.failed(ApiErrorCode.fromCode(ApiErrorCode.ILLEGAL_ARGUMENT.getCode()));
    }

}
